home *** CD-ROM | disk | FTP | other *** search
- /*
- * #include <legal/bs.h>
- >
- > Copyright (c) 1989 Washington University in Saint Louis, Missouri and
- > Chris Myers. All rights reserved.
- >
- > Permission is hereby granted to copy, reproduce, redistribute or
- > otherwise use this software as long as: (1) there is no monetary
- > profit gained specifically from the use or reproduction of this
- > software, (2) it is not sold, rented, traded, or otherwise marketed,
- > (3) the above copyright notice and this paragraph is included
- > prominently in any copy made, and (4) that the name of the University
- > is not used to endorse or promote products derived from this software
- > without the specific prior written permission of the University.
- > THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- > IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- > WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
- >
- */
-
- #include "defs.h"
- extern int errno;
-
- /*************************************************************************/
- /* FUNCTION : kill_children */
- /* PURPOSE : Kills all outstanding transmitters. Kill_children will */
- /* wait thirty seconds to allow all of the transmitters to */
- /* exit gracefully. If invoked by a signal (SIGTERM?), exit */
- /* else return to caller. */
- /* ARGUMENTS : Signal number or 0 if to return to caller */
- /*************************************************************************/
-
- void
- kill_children(sig)
- int sig; /* sig will be nonzero if called by SIGTERM */
-
- {
- struct host *hostptr;
-
- if ((hostlist == NULL) && (sig == 0)) return;
-
- log(LOG_INFO, "newsxd: shutting down all transmitters\n");
-
- foreach (hostptr, hostlist) {
- if (hostptr->pid != 0) {
- dprintf("newsxd: killing transmitter for %s\n", hostptr->hostname);
- (void) kill (hostptr->pid, SIGTERM);
- }
- }
-
- if (sig == 0) return;
-
- dprintf("(kill_children): sleeping 30 seconds\n");
-
- (void) sleep(30); /* allow transmitters to quit gracefully */
-
- log(LOG_INFO, "shut down by signal %d\n", sig);
-
- if (debug == 0 && DEBUG == 0) {
- (void) unlink(pidfile);
- (void) unlink(statusfile);
- }
-
- (void) exit(0); /* invoked by SIGTERM, time to quit */
-
- }
-
- /*************************************************************************/
- /* FUNCTION : xmit_done */
- /* PURPOSE : Catch the SIGCHLD from completed transmitters and do the */
- /* necessary cleanup. Also wait synchronously for a xmitter */
- /* to complete; also perform forced cleanup for an xmitter */
- /* ARGUMENTS : sig, > 0 if xmit_done is called by SIGCHLD */
- /* == 0 if called to check for a zombie w/NOHANG */
- /* ==-1 if called to sync wait for a transmitter */
- /* < -1 to perform a forced cleanup on a transmitter */
- /*************************************************************************/
-
- void
- xmit_done(sig)
- int sig; /* sig will be nonzero if called by SIGCHLD */
-
- {
- struct host *hostptr;
- struct class *classptr;
- union wait status;
- struct rusage usage;
- extern char *sys_errlist[];
-
- int options,
- loop,
- pid;
-
- (void) sigsetmask(0);
-
- /*
- * Try and catch any completed transmitters first and process them.
- * Then, if xmit_done() was called to synchronously wait on a transmitter
- * or to perform forced cleanup on a transmitter, do it.
- */
-
- Dprintf("(xmit_done) sig %d\n", sig);
-
- while (1) {
- if (sig != -1) options = WNOHANG;
-
- pid = wait3(&status, options, &usage);
-
- if (pid <= 0)
- if (sig < -1) {
- pid = -sig;
- sig = 0;
- } else {
- Dprintf("(xmit_done) no process! pid=%d, errno=%s\n", pid,
- sys_errlist[errno]);
- return;
- }
-
- Dprintf("(xmit_done) pid %d\n", pid);
-
- for (loop = 0; loop < MAXXMITTERS; loop++) {
- if (pidlist[loop] == pid) {
- hostptr = pidmap[loop];
- pidlist[loop] = 0;
- pidmap[loop] = (struct host *) NULL;
- hostptr->pid = 0;
- hostptr->whynot = WN_NOTMYTURN;
- foreach (classptr, classlist) {
- if (strcmp(hostptr->class, classptr->classname) == 0) {
- classptr->curxmits--;
- freeclassslot(classptr, hostptr->classslot);
- dprintf("%s: transmission completed\n", hostptr->hostname);
- return;
- }
- }
-
- /*
- * The (sig < -1) check is here just in case a transmitter exitted
- * normally just as run_queue tries to force a cleanup on it. If
- * this isn't here, newsxd will abort with a corrupted data err.
- */
-
- if (sig < -1) return;
-
- /* Something is seriously wrong here -- quit somewhat gracefully */
-
- logerr("CORRUPTED HOST/CLASS STRUCTURE!\n");
- kill_children(1);
- }
- }
- }
- }
-
- /*************************************************************************/
- /* FUNCTION : run_queue */
- /* PURPOSE : Go through the list of hosts to transmit to and start any */
- /* transmitter that meets all of the necessary conditions */
- /* ARGUMENTS : none */
- /*************************************************************************/
-
- void
- run_queue()
-
- {
- struct host *hostptr;
- struct class *classptr, *lastclass;
- struct stat statbuf;
- struct tm *curtime;
-
- int clock,
- pid,
- hadtheirchance,
- which,
- which2,
- loop,
- loop2,
- hostargc,
- classargc;
-
- char fnbuf[MAXPATHLEN],
- fnbuf2[MAXPATHLEN],
- **hostargv,
- **classargv;
-
- CONFIGCHANGED = 0;
- hadtheirchance = 0;
- lastclass = (struct class *) NULL;
-
- foreach (hostptr, hostlist) {
-
- /*
- * If the configuration has been changed (possibly via SIGHUP), we
- * need to assume that all of the current pointers and such are
- * invalid since the current host MAY have been deleted...
- *
- * There is still a race condition, but this check lessens the chance
- * of problems considerably.
- *
- */
-
- if (CONFIGCHANGED) {
- dprintf("Reconfigured during queue run -- aborting queue run\n");
- CONFIGCHANGED = 0;
- return;
- }
-
- (void) time(&clock);
- curtime = localtime(&clock);
-
- classptr = getclass(hostptr->class);
-
- if (classptr != lastclass) {
- if (lastclass) {
- dprintf("class %s: members %d, hadtheirchance %d\n",
- lastclass->classname, lastclass->members, hadtheirchance);
- if (lastclass->members == hadtheirchance) {
- lastclass->xmitsernum++;
- dprintf("class %s: add 1 to xmitsernum, now %d\n",
- lastclass->classname, lastclass->xmitsernum);
- }
- }
- hadtheirchance = 0;
- lastclass = classptr;
- }
-
- /*
- * Check and see if we somehow missed the SIGCHLD for a transmitter and
- * it's really gone and we don't know it. If so, force a cleanup.
- */
-
- if ((hostptr->pid) && (kill(hostptr->pid, 0) == -1) && (errno == ESRCH)) {
- xmit_done(-hostptr->pid);
- hostptr->pid = 0; /* Just in case xmit_done() missed it! */
- }
-
- /*
- * Check to see if the host has had a transmitter running for more
- * than the ttl of its transmission class. If so, kill it.
- */
-
- which = (hostptr->options.ttl) ?
- hostptr->options.ttl : classptr->options.ttl;
- which2 = (hostptr->options.ttlpenalty) ?
- hostptr->options.ttlpenalty : classptr->options.ttlpenalty;
-
- dprintf("%s: checking ttl (%d/%d)\n", hostptr->hostname, which, which2);
-
- if ((hostptr->pid > 0) && (clock > (hostptr->lasttime + which))) {
- dprintf("%s: exceeded ttl, killing\n");
- hostptr->penaltytime = clock + which2;
- if (kill(hostptr->pid, SIGTERM) != 0) xmit_done(-hostptr->pid);
- hostptr->whynot = WN_TTL;
- continue; /* skip this host to give others a chance */
- }
-
- /*
- * If there is already a running transmitter for this host, skip it. We
- * don't want more than one!
- */
-
- dprintf("%s: checking for active daemon (%d)\n", hostptr->hostname,
- hostptr->pid);
-
- if (hostptr->pid > 0) {
- hostptr->whynot = WN_RUNNING;
- hadtheirchance++;
- continue;
- }
-
- /*
- * Check to see if this host has already had a chance to start
- * a transmitter. If so, let someone else have a chance.
- */
-
- dprintf("%s: xmitsernum is %d, class xmitsernum is %d\n",
- hostptr->hostname, hostptr->xmitsernum, classptr->xmitsernum);
-
- /*
- if ((hostptr->xmitsernum == classptr->xmitsernum) &&
- (classptr->maxxmits < classptr->members)) {
- */
-
- if (hostptr->xmitsernum == classptr->xmitsernum) {
- hadtheirchance++;
- /* hostptr->whynot = WN_NOTMYTURN; */
- continue;
- }
-
- /*
- * Check the current time against the last time an xmit was started
- * for this class to make sure we don't start too many daemons at
- * one time.
- */
-
- which = (hostptr->options.startint) ?
- hostptr->options.startint : classptr->options.startint;
-
- dprintf("%s: checking class startup interval (clock is %d, start %d)\n",
- hostptr->hostname, clock, (classptr->laststart + which));
-
- if (clock < (classptr->laststart + which)) {
- hostptr->whynot = WN_CLASSSTARTINT;
- continue;
- }
-
- /*
- * Check the current load and compare against the maximum allowed
- * load for starting new transmitters for this hosts's class.
- */
-
- which = (hostptr->options.maxload) ?
- hostptr->options.maxload : classptr->options.maxload;
-
- dprintf("%s: checking maximum load (cur %d, max %d)\n",
- hostptr->hostname, getla(), which);
-
- if (getla() > which) {
- hostptr->whynot = WN_LOAD;
- continue;
- }
-
- /*
- * Check the number of currently running transmitters for this host's
- * transmission class. If we're already running at the limit, skip
- * this host.
- */
-
- dprintf("%s (%s): checking running xmit count (%d of %d)\n",
- hostptr->hostname, classptr->classname, classptr->curxmits,
- classptr->maxxmits);
-
- if (classptr->curxmits >= classptr->maxxmits) {
- hostptr->whynot = WN_MAXXMITS;
- continue;
- }
-
- /*
- * All tests after this point should be tests for some characteristic
- * of the host that would cause it to avoid its turn to start its
- * transmitter. Things like: the host startup interval hasn't passed
- * yet, or it's a bad time to send to that host, or there's no work
- * to send to that host.
- *
- * Tests before this point should be tests for some characteristic not
- * related to the particular host that prevents it from being able to
- * start its transmitter. Things like: load too high, too many xmits
- * already running...
- */
-
- /*
- * This host had a chance to send news (and can possibly skip it).
- */
-
- hostptr->xmitsernum = classptr->xmitsernum;
- hadtheirchance++;
-
- /*
- * Check to see if the host had a transmitter killed because it ran
- * longer than its class' ttl. If so, see if its penalty time is
- * still unexpired.
- */
-
- dprintf("%s: checking ttl penalty\n", hostptr->hostname);
-
- if (hostptr->penaltytime > clock) {
- hostptr->whynot = WN_PENALTYTIME;
- continue;
- }
-
- /*
- * Check the current time against the last time an xmit was started
- * for this class to make sure we don't start a transmitter for this
- * host too often...
- */
-
- which = (hostptr->options.interval) ?
- hostptr->options.interval : classptr->options.interval;
-
- dprintf("%s: checking host startup interval (%d)\n", hostptr->hostname,
- which);
-
- if (clock < (hostptr->lasttime + which)) {
- hostptr->whynot = WN_HOSTSTARTINT;
- continue;
- }
-
- /*
- * Check to see that the current time is within the permitted range
- * of transmission times. If not, skip this host.
- */
-
- dprintf("%s: checking valid transmission times (%s)\n",
- hostptr->hostname, hostptr->times);
-
- if (!validtime(hostptr->times)) {
- hostptr->whynot = WN_BADTIME;
- continue;
- }
-
- /*
- * See if there is an outstanding work file for this host, if so we
- * just need to run a transmitter, otherwise rename the batch file
- * to the work file and run the transmitter.
- */
-
- (void) sprintf(fnbuf, workfile, hostptr->hostname);
-
- dprintf("%s: checking for workfile (%s)\n",
- hostptr->hostname, fnbuf);
-
- if ((loop = stat(fnbuf, &statbuf)) != 0) {
- Dprintf("%s: stat(%s) returned %d (errno is %d)\n", hostptr->hostname,
- fnbuf, loop, errno);
-
- (void) sprintf(fnbuf2, batchfile, hostptr->hostname);
-
- dprintf("%s: checking for batchfile (%s)\n",
- hostptr->hostname, fnbuf2);
-
- if (!classptr->flags[C_NOBATCH]) {
- if ((loop = stat(fnbuf2, &statbuf)) != 0) {
- Dprintf("%s: stat(%s) returned %d (errno is %d)\n",
- hostptr->hostname, fnbuf, loop, errno);
-
- hostptr->whynot = WN_NOWORK;
- continue;
- } else {
- hostptr->whynot = WN_RENAMEFAILED;
- if (!classptr->flags[C_NOWORK] && rename(fnbuf2, fnbuf) != 0) {
- dprintf("%s: rename failed (%s to %s)\n",
- hostptr->hostname, fnbuf2, fnbuf);
- continue;
- }
- }
- }
- }
-
- /*
- * Fork off a transmitter for this host
- */
-
- hostptr->whynot = WN_RUNNING;
-
- if ((hostptr->classslot = getclassslot(classptr)) == -1) {
- hostptr->whynot = WN_NOSLOT;
- exit(1);
- }
-
- if ((pid = fork()) == 0) {
- /* Child. */
- int fd;
-
- /* Untrap all of the signals for newsxd; this is now a transmitter */
-
- (void) signal(SIGCHLD, SIG_DFL);
- (void) signal(SIGHUP, SIG_DFL);
- (void) signal(SIGQUIT, SIG_DFL);
- (void) signal(SIGTERM, SIG_DFL);
- (void) signal(SIGUSR1, SIG_DFL);
- (void) signal(SIGUSR2, SIG_DFL);
- (void) signal(SIGIO, SIG_DFL);
- (void) signal(SIGIOT, SIG_DFL);
- (void) signal(SIGTRAP, SIG_DFL);
-
- fd = open(classptr->flags[C_NOWORK] ? fnbuf2 : fnbuf, O_RDONLY);
- if (fd > 0) (void) flock(fd, LOCK_EX);
-
- #ifdef CNEWSLOCKING
- if (!classptr->flags[C_NOWORK]) {
-
- /* If we can get a valid lock then we know that since the batch
- * file has been renamed to WORKFILE, cnews will now be writing
- * to a different file (BATCHFILE).
- */
- (void) newslock();
- (void) newsunlock();
- }
- #endif CNEWSLOCKING
-
- Dprintf("(%s): classslot is %d\n", hostptr->hostname,
- hostptr->classslot);
-
- dprintf("%s: spawning transmitter\n", hostptr->hostname);
-
- if (!newsxdebug) {
- (void) sprintf(fnbuf2, xmitlogs, hostptr->hostname);
- (void) freopen(fnbuf2, "a", stdout);
- (void) freopen(fnbuf2, "a", stderr);
- }
-
- (void) fprintf(stderr, "%s: begin at %02d:%02d:%02d\n",
- hostptr->hostname, curtime->tm_hour, curtime->tm_min,
- curtime->tm_sec);
- (void) fflush(stderr);
-
- which = (hostptr->options.deltanice) ?
- hostptr->options.deltanice : classptr->options.deltanice;
-
- if (which != 0) {
- Dprintf("Changing transmitter nice by %d for %s\n",
- which, hostptr->hostname);
-
- (void) nice(which);
- }
-
- if (classptr->xargc == 0) {
- classptr = getclass("DEFAULT");
- if (classptr->xargc == 0) {
- logerr("host %s: No DEFAULT xmitter defined -- aborting\n",
- hostptr->hostname);
- (void) _exit(1);
- }
- }
-
- Dprintf("classptr->xargc = %d\n", classptr->xargc);
-
- Dprintf("%s: EXECing %s with parameters:\n",
- hostptr->hostname, classptr->xpath);
-
- for (loop = 0; loop <= classptr->xargc; loop++) {
- if (classptr->xargv[loop] == NULL) {
- Dprintf("arg[%d] = NULL\n", loop);
- } else {
- Dprintf("arg[%d] = %s\n", loop, classptr->xargv[loop]);
- }
- }
-
- classargc = classptr->xargc;
- classargv = classptr->xargv;
-
- classargv[classargc] = NULL;
-
- for (loop = 0; loop < classargc; loop++) {
- hostargv = NULL;
- if (strcmp(classargv[loop], "%f") == NULL) {
- hostargc = hostptr->xargc;
- hostargv = hostptr->xargv;
- }
-
- if (strcmp(classargv[loop], "%d") == NULL) {
- struct class *myclassptr = getclass(hostptr->class);
- hostargc = myclassptr->debugargc;
- hostargv = myclassptr->debugargv;
- Dprintf("debug argc = %d\n", hostargc);
- }
-
- if (hostargv != NULL) {
- if (hostargc == 0) {
- classargc--;
- for (loop2 = loop; loop2 < MAXEXECARGS-1; loop2++)
- classargv[loop2] = classargv[loop2 + 1];
- } else {
- classargc += hostargc - 1;
- for (loop2 = MAXEXECARGS - hostargc; loop2 > loop; loop2--)
- classargv[loop2 + hostargc - 1] = classargv[loop2];
- for (loop2 = loop; loop2 < loop + hostargc; loop2++)
- classargv[loop2] = hostargv[loop2 - loop];
- loop += hostargc - 1;
- }
- }
- }
-
- Dprintf("(after host flag insertion) classargc = %d\n",
- classargc);
-
- for (loop = 0; loop <= classargc; loop++) {
- /* WARNING: we are mangling the xargv array here! */
- processarg(loop, hostptr, classptr);
-
- if (classargv[loop] == NULL) {
- Dprintf("arg[%d] = NULL\n", loop);
- } else {
- Dprintf("arg[%d] = %s\n", loop, classargv[loop]);
- }
- }
-
- (void) execvp(classptr->xpath, classargv);
-
- logerr("%s: can't exec %s\n", hostptr->hostname, classptr->xpath);
-
- (void) _exit(1); /* could not exec the news transmitter! */
- } else {
- /* Parent. */
- if (pid == -1) {
- logerr("host %s: Can't fork child.\n", hostptr->hostname);
- continue;
- }
- for (loop = 0; loop < MAXXMITTERS; loop++) {
- if (pidlist[loop] == 0) {
- pidlist[loop] = pid;
- pidmap[loop] = hostptr;
- Dprintf("%s: entered into pidmap/list at %d\n",
- hostptr->hostname, loop);
- break;
- }
- }
- hostptr->pid = pid;
- hostptr->lasttime = clock;
- classptr->laststart = clock;
- classptr->curxmits++;
- if (newsxdebug) xmit_done(-1); /* wait for xmitter to complete */
- }
- }
-
- if (classptr->members == hadtheirchance) {
- dprintf("class %s: add 1 to sernum (members %d, hadchance %d)\n",
- classptr->classname, classptr->members, hadtheirchance);
- classptr->xmitsernum++;
- }
- }
-